/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.nio.channels.spi;

import java.io.IOException;
import java.net.ProtocolFamily;
import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
import sun.security.action.GetPropertyAction;


/**
 * 用于选择器和可选择通道的服务提供程序类。
 *
 * <p> 选择器提供程序是这个类的一个具体子类，它有无参构造函数，并实现下面指定的抽象方法。
 * Java虚拟机的给定调用，维护一个单系统范围的默认提供程序实例，该实例由provider方法返回。
 * 该方法的第一次调用将定位默认提供程序，如下所示。
 *
 * <p> 系统默认提供程序由DatagramChannel、Pipe、Selector、ServerSocketChannel和SocketChannel类的静态open方法使用。
 * 它也被System.inheritedChannel()方法使用。
 * 程序可以使用默认提供程序之外的提供程序，通过实例化该提供程序，然后直接调用该类中定义的open方法。
 *
 * <p> 这个类中的所有方法对于多个并发线程使用都是安全的。
 *
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class SelectorProvider {

    private static final Object lock = new Object();
    
    // 内部保存一个静态的provider
    private static SelectorProvider provider = null;

    /**
     * 初始化这个类的实例
     *
     * @throws  SecurityException
     *          If a security manager has been installed and it denies
     *          {@link RuntimePermission}<tt>("selectorProvider")</tt>
     */
    protected SelectorProvider() {
    	// 检查能否使用selectorProvider
        SecurityManager sm = System.getSecurityManager();
        if (sm != null)
            sm.checkPermission(new RuntimePermission("selectorProvider"));
    }

    private static boolean loadProviderFromProperty() {
        String cn = System.getProperty("java.nio.channels.spi.SelectorProvider");
        if (cn == null)
            return false;
        try {
        	// 调用系统加载器，调用它的无参构造器，赋值给provider
            Class<?> c = Class.forName(cn, true,
                                       ClassLoader.getSystemClassLoader());
            provider = (SelectorProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(null, x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(null, x);
        }
    }

    private static boolean loadProviderAsService() {

        ServiceLoader<SelectorProvider> sl =
            ServiceLoader.load(SelectorProvider.class,
                               ClassLoader.getSystemClassLoader());
        Iterator<SelectorProvider> i = sl.iterator();
        for (;;) {
            try {
                if (!i.hasNext())
                    return false;
                provider = i.next();
                return true;
            } catch (ServiceConfigurationError sce) {
                if (sce.getCause() instanceof SecurityException) {
                    // Ignore the security exception, try the next provider
                    continue;
                }
                throw sce;
            }
        }
    }

    /**
     * 返回此Java虚拟机调用的系统范围默认选择器提供程序。
     *
     * <p> 这个方法的第一次调用，定位了默认的provider对象，如下所示:</p>
     *
     * <ol>
     *
     *   <li><p> 如果系统属性为java.nio.channels.spi.SelectorProvider被定义，然后它被当作一个具体提供程序类的完全限定名。
     *   类被加载并实例化;
     *   如果此过程失败，则抛出一个未指定的错误。</p></li>
     *
     *   <li><p> 如果提供程序类已安装在系统类加载器可见的jar文件中，并且该jar文件包含一个名为java.nio.channels.spi.SelectorProvider的提供程序配置文件，
     *   在资源目录 META-INF/services，然后获取该文件中指定的第一个类名称。
     *   类被加载并实例化;
     *   如果此过程失败，则抛出一个未指定的错误。</p></li>
     *
     *   <li><p> 最后，如果上述任何一个都没有指定提供程序，则实例化系统默认的提供程序类并返回结果。</p></li>
     *
     * </ol>
     *
     * <p> 此方法的后续调用将返回第一次调用所返回的提供者。 </p>
     *
     * @return  The system-wide default selector provider
     */
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                    	    // 先从系统属性获得，再从service获得，最后返回默认provider
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

    /**
     * 打开数据报通道。
     *
     * @return  The new channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public abstract DatagramChannel openDatagramChannel()
        throws IOException;

    /**
     * 打开数据报通道。
     *
     * @param   family
     *          The protocol family
     *
     * @return  A new datagram channel
     *
     * @throws  UnsupportedOperationException
     *          If the specified protocol family is not supported
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @since 1.7
     */
    public abstract DatagramChannel openDatagramChannel(ProtocolFamily family)
        throws IOException;

    /**
     * 打开管道。
     *
     * @return  The new pipe
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public abstract Pipe openPipe()
        throws IOException;

    /**
     * 打开一个选择器
     *
     * @return  The new selector
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public abstract AbstractSelector openSelector()
        throws IOException;

    /**
     * 打开一个服务器-套接字通道。
     *
     * @return  The new channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public abstract ServerSocketChannel openServerSocketChannel()
        throws IOException;

    /**
     * 打开套接字通道。
     *
     * @return  The new channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public abstract SocketChannel openSocketChannel()
        throws IOException;

    /**
     * 返回从创建此java虚拟机的实体继承的通道。
     *
     * <p> 在许多操作系统上，进程(如Java虚拟机)的启动方式允许进程从创建该进程的实体继承一个通道。
     * 这样做的方式是依赖于系统的，因为是可能的实体，该通道可能被连接。
     * 例如，在UNIX系统上，Internet服务守护进程(inetd)用于启动程序，在请求到达关联的网络端口时为请求提供服务。
     * 在本例中，启动的进程继承表示网络套接字的通道。
     *
     * <p> 在继承的通道代表一个网络套接字的情况下，
     * 然后通道类型由方法返回，如下:
     *
     * <ul>
     *
     *  <li><p> 如果继承的通道表示面向流的连接套接字，则返回SocketChannel。
     *  套接字通道(至少在开始时)在阻塞模式下绑定到套接字地址，并连接到对等点。
     *  </p></li>
     *
     *  <li><p> 如果继承的通道表示面向流侦听套接字，则返回ServerSocketChannel。
     *  服务器-套接字通道(至少在开始时)处于阻塞模式，并绑定到套接字地址。
     *  </p></li>
     *
     *  <li><p> 如果继承的通道是面向数据的套接字，那么返回一个DatagramChannel。
     *  数据报通道至少在初始阶段处于阻塞模式，并绑定到一个套接字地址。
     *  </p></li>
     *
     * </ul>
     *
     * <p> 除了所描述的面向网络的通道之外，这种方法将来还可能返回其他类型的通道。
     *
     * <p> 该方法的第一次调用将创建返回的通道。该方法的后续调用将返回相同的通道。</p>
     *
     * @return  The inherited channel, if any, otherwise <tt>null</tt>.
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @throws  SecurityException
     *          If a security manager has been installed and it denies
     *          {@link RuntimePermission}<tt>("inheritedChannel")</tt>
     *
     * @since 1.5
     */
   public Channel inheritedChannel() throws IOException {
        return null;
   }

}
